package calendar;

import edu.gmu.client.FrontEnd;
import java.awt.Dimension;
import java.io.IOException;
import java.util.Date;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.plaf.metal.DefaultMetalTheme;
import javax.swing.plaf.metal.MetalLookAndFeel;

/* main calendar client class */
public class CalendarClient implements CalEventListener
{
    ClientPanel clientPanel;
    CalendarObjInt calendarObjInt = null;
    Callback callback = null;
    User user;
    User currentUser;
    FrontEnd frontEnd = null;
  
    public CalendarClient( ) throws IOException
    {
        super();
        
        frontEnd = new FrontEnd();
        callback = new Callback();
    }

    /* set the client panel object for the client object */
    public void setClientPanel( ClientPanel clientPanel )
    {
        this.clientPanel = clientPanel;
        this.clientPanel.setUsers( this.list() );
        callback.setClientPanel( this.clientPanel );
        callback.setClient( this );
    }

    public void reconnect()
    {
      try
      {

        connectCalendar ( this.currentUser );
        clientPanel.setCalendar(calendarObjInt);
        calendarObjInt.registerCallback( callback );
        clientPanel.setConnectName( calendarObjInt.getUser().getName() );
        clientPanel.addAlert("Reconnected to "
          + this.currentUser.getName() + "'s calendar.  " +
          "Check the boxes next to user names to see their event lists." );
      }
      catch (Exception e)
      {
          System.out.println( "(ERROR) Reconnect failed -> " + e );
      }
    }

    /* connect to a calendar via the remote calendar object interface */
    public CalendarObjInt connectCalendar( User user )
    {
        try
        {
            // Disconnect from previous??
            if( calendarObjInt != null )
            {
                //System.out.println( "Disconnecting from calendar for " + user );
                this.disconnectCalendar(calendarObjInt.getUser());
                calendarObjInt = null;
            }
            calendarObjInt = this.frontEnd.connectCalendarObj(user);
            this.currentUser = user;
        }
        catch (Exception e)
        {
            System.out.println( "(ERROR) Invalid calendar id specified (calendar) -> " + e );
            e.printStackTrace();
        }
        return calendarObjInt;
    }

    /* disconnect from a remote calendar object */
    public boolean disconnectCalendar( User user )
    {
        boolean result = false;
        try
        {
            if(calendarObjInt!=null)
            {
                calendarObjInt.unregisterCallback();
                calendarObjInt = null;
                this.currentUser = null;
            }
            result = this.frontEnd.disconnectCalendar( user );
        }
        catch (Exception e)
        {
            System.out.println("CalendarClient: disconnectCalendar -> " + e);
            e.printStackTrace();
        }

        return result;
    }

    /* create a new remote calendar object via the remote calendar manager */
    public boolean createCalendar( User user )
    {
        boolean result = false;
        try
        {
            result = this.frontEnd.createCalendar( user );
        }
        catch (Exception e)
        {
            System.out.println("CalendarClient: createCalendar -> " + e);
            System.exit(1);
        }
        return result;
    }

    /* schedule an event in the calendar */
    public ArrayList scheduleEvent( int type, String desc, Date start, Date end,
                          ArrayList users, User owner, Event oldEvent )
    {
        boolean addToThis = true;

        Event event = new Event( type, desc, start, end, owner );

        if ( type == 3 ) addToThis = false;

        ArrayList result = new ArrayList();
        try
        {
            if( calendarObjInt != null && calendarObjInt.isConnected() )
            {
                result = calendarObjInt.scheduleEvent( users, event, oldEvent, addToThis );
            }
        }
        catch(IOException ex)
        {
            System.out.println("CalendarClient: scheduleEvent -> " + ex);
        }

        return result;
    }

    /* retreive a list of events based on user and start/end dates */
    public ArrayList retrieveEvents( User user, Date start, Date end )
    {
        ArrayList result = new ArrayList();
        try
        {
            if( calendarObjInt != null && calendarObjInt.isConnected() )
            {
                result = calendarObjInt.retrieveEvents( user, start, end );
            }
        }
        catch(IOException ex)
        {
            System.out.println("CalendarClient: retrieveEvents -> " + ex);
        }

        System.out.println( "number of events: " + result.size() );

        return result;
    }

    /* returns the current list of calendars (users) */
    public ArrayList list()
    {
        ArrayList users = new ArrayList();
        try
        {
            users = this.frontEnd.list();
            //users = RemCalendarMgr.list();
        }
        catch (Exception e)
        {
            System.out.println( "Client: Unable to connect to Calendar Server." );
            if ( this.isConnected() )
            {
              try
              {
                boolean connected = false;
                try
                {
                    connected = this.calendarObjInt.isConnected(); 
                }
                catch(IOException ioe)
                {
                    
                }
                
                if(connected == false) clientPanel.setReconnectName( this.currentUser.getName() );
                Thread.sleep( 5000 );
                if( connected == false )
                {
                    System.out.println( "Client: Reconnecting to Calendar Server." );
                    this.calendarObjInt = null;
                    this.reconnect();
                }
              }
              catch ( Exception ex )
              {
                System.out.println( "CalendarClient: list-> " + ex );
              }
            }
            //e.printStackTrace();
        }

        return users;
    }

    /* returns status of this client's connection */
    public boolean isConnected()
    {
        return (this.calendarObjInt != null);
    }
  
    public User findUser( User user )
    {
        ArrayList users = this.list();
        User theUser = null;
        for (int i = 0; i < users.size(); i++)
        {
            User aUser = (User)users.get(i);
            if ( aUser.getId() == user.getId() )
            {
                theUser = aUser;
                break;
            }
        }  
        return theUser;
    }

  public void calEventOccurred( CalEvent calEvent ) {

    /* all events end up here, forwarded to appropriate remote object */
    if ( calEvent.getEventId() == CalEvent.CLOSE )
    {
        try
        {
            if ( this.calendarObjInt != null )
            {
                User aUser = this.calendarObjInt.getUser();
                this.disconnectCalendar(aUser);
            } 
        } 
        catch (IOException ex)
        {
            System.out.println("CalendarClient: calEvent(CLOSE) -> " + ex);
        }
        System.exit( 0 );
    }

    /* perform necessary actions to create a new calendar */
    else if ( calEvent.getEventId() == CalEvent.CREATE )
    {
        String alert = "";
        String userName = clientPanel.getUserName();
        user = User.makeUser( userName );
        boolean result = createCalendar( user );
        if ( result )
        {
            alert = "Created new calendar for '" + user.getName() +
            "'.  Your user id is:  " + user.getId();
            clientPanel.setUsers( list() );
            clientPanel.clearCreateText();
        }
        else
        {
            alert = "Error:  Could not create calendar for '" + user.getName() +
            "'. This name is already in use (or you entered nothing)." +
            " Please try again.";
        }
        clientPanel.addAlert( alert );
    }

    /* perform necessary actions to connect to a calendar */
    else if ( calEvent.getEventId() == CalEvent.CONNECT )
    {
        User panelUser = clientPanel.getUser();
        if ( panelUser == null )
        {
            String message = "Error:  You must enter a user from the list ";
            clientPanel.addAlert( message );
            return;
        }

        /* connect to the specified calendar and register for callbacks */
        try
        {
            connectCalendar(panelUser);
            clientPanel.setCalendar(calendarObjInt);
            clientPanel.resetEventList();
            clientPanel.clearEventPanel();

            calendarObjInt.registerCallback( callback );
            clientPanel.setConnectName( calendarObjInt.getUser().getName() );
            /*clientPanel.setUserChecked( calendar.getUser() );*/
            clientPanel.addAlert("Connected to "
                + calendarObjInt.getUser().getName() + "'s calendar.  " +
                "Check the boxes next to user names to see their event lists." );
        }
        catch (Exception ex)
        {
            clientPanel.addAlert(   "Error:  Cannot connect due to invalid user" +
                                    " id or someone already connected to this calendar");
        }

        /* clear out the users selected and the status text at the top */
        clientPanel.clearSelectedUsers();
        clientPanel.clearConnectText();
    }

    /* perform necessary actions to disconnect from a calendar */
    else if ( calEvent.getEventId() == CalEvent.DISCONNECT )
    {
        User dUser =  dUser = clientPanel.getUser();

        if( dUser == null && this.calendarObjInt != null )
        {
            try
            {
                dUser = this.calendarObjInt.getUser();
            }
            catch( IOException ioe )
            {
                // ignore - should never happen anyway
            }
        }
        
        if( dUser == null )
        {
            clientPanel.addAlert( "Error:  You need to enter a user." );
        }
        else
        {
            boolean result = disconnectCalendar( dUser );
            if ( result )
            {
                clientPanel.setDisconnected();
                clientPanel.resetEventList();
                clientPanel.clearEventPanel();
                clientPanel.addAlert("Disconnected from " + dUser + "'s calendar.");
            }
            else
            {
                clientPanel.addAlert( "Error:  You entered an invalid user id " +
                              "or you are not connected to this calendar." );
            }
        }

        /* clear out necessary windows from client panel */
        clientPanel.clearSelectedUsers();
        clientPanel.clearEventPanel();
    }

    /* perform necessary actions to list the user calendars */
    else if ( calEvent.getEventId() == CalEvent.LIST_USERS )
    {
        clientPanel.setUsers( list() );
    }

    /* perform necessary actions to list the events of the selected users */
    else if ( calEvent.getEventId() == CalEvent.LIST_EVENTS )
    {
        if ( this.isConnected() )
        {
            clientPanel.resetEventList();
            ArrayList selectedUsers = clientPanel.getSelectedUsers();
            clientPanel.listUserEvents( selectedUsers );
        }
    }

    /* perform necessary actions to show a selected event */
    else if ( calEvent.getEventId() == CalEvent.SHOW_EVENT )
    {
        if ( this.isConnected() )
        {
            try
            {
                clientPanel.showEvent(clientPanel.getSelectedEvent(),
                clientPanel.getCalendar().getUser() );
            }
            catch(IOException ex)
            {
                System.out.println("CalendarClient: calEvent(SHOW_EVENT) -> " + ex);
            }
        }
    }

    /* perform the necessary actions to create or edit an event */
    else if ( calEvent.getEventId() == CalEvent.CREATE_EVENT ||
              calEvent.getEventId() == CalEvent.EDIT_EVENT )
    {
      String message = new String();

      /* check to see if currently connected to a calendar */
      if ( !this.isConnected() )
      {
        message = "Error:  You must connect to a calendar before you can " +
          "add any events.";
        clientPanel.addAlert( message );
        return;
      }
      
      ArrayList usersBooked = new ArrayList();
      ArrayList selectedUsers = new ArrayList();
      try
      {
        currentUser = clientPanel.getCalendar().getUser();
      }
      catch (IOException ex)
      {
          System.out.println("CalendarClient: calEvent(CREATE/EDIT) -> " + ex);
      }

      Event oldEvent = null;
      /* check for permission to edit an event */
      if ( calEvent.getEventId() == CalEvent.EDIT_EVENT )
      {
        boolean permission = false;
        oldEvent = clientPanel.getSelectedEvent();
        if ( oldEvent.getOwner().equals(currentUser) ||
             (oldEvent.getType() == 3 &&
             oldEvent.isParticipant( currentUser ) ) )
          permission = true;

        if ( !permission )
        {
          message = "Error:  You do not have permission to edit this event.";
          clientPanel.addAlert( message );
          return;
        }
      }

      /* check for CREATE_EVENT event and buttons are disabled */
      if ( calEvent.getEventId() == CalEvent.CREATE_EVENT &&
           clientPanel.areCreateEventPanelButtonsDisabled() )
      {
        message = "Error:  The event type buttons are disabled.  They have " +
          "been re-enabled.  Please try again.";
        clientPanel.addAlert( message );
        clientPanel.setCreateEventPanelButtonsEnabled();
        clientPanel.resetEventList();
        return;
      }

      /* check for unselected type */
      if ( clientPanel.getNewEventType() == 0 )
      {
        message = "Error:  You did not select an event type. Please try again.";
        clientPanel.addAlert( message );
        return;
      }

      /* check for a start date that has already passed */
      Calendar cal = Calendar.getInstance();
      Date now = cal.getTime();
      if ( clientPanel.getNewEventStartDate().compareTo( now ) <= 0 )
      {
        message = "Error:  The start date cannot be in the past. " +
          "Please try again.";
        clientPanel.addAlert( message );
        return;
      }

      /* check for end date before start date */
      Date temp = new Date();
      temp = clientPanel.getNewEventStartDate();
      if ( clientPanel.getNewEventEndDate().compareTo( temp ) <= 0 )
      {
        message = "Error:  The event end date must be after the event start " +
          "date. Please try again.";
        clientPanel.addAlert( message );
        return;
      }

      /* check for users selected if group event selected */
      if ( clientPanel.getNewEventType() == 3 )
      {
        boolean selfSelected = false;
        Iterator itr = clientPanel.getSelectedUsers().iterator();
        while ( itr.hasNext() )
        {
          User u = (User)itr.next();
          if ( u.getId() == currentUser.getId() ) selfSelected = true;
        }
        if ( selfSelected && clientPanel.getSelectedUsers().size() == 1 )
        {
          message = "Error:  You selected a group event, but did not select " +
            "any participants besides yourself.";
          clientPanel.addAlert( message );          
          return;
        }
        else if ( clientPanel.getSelectedUsers().isEmpty() )
        {
          message = "Error:  You selected a group event, but did not select " +
            "any participants.";
          clientPanel.addAlert( message );
          return;
        }
      }

      /* get the users selected to be in this event if it's a group event */
      if ( clientPanel.getNewEventType() == 3 )
      {
        selectedUsers = clientPanel.getSelectedUsers();
        Iterator itrUsers = selectedUsers.iterator();
        boolean selfSelected = false;
        while ( itrUsers.hasNext() )
        {
          User u = (User)itrUsers.next();
          if ( u.getId() == currentUser.getId() ) selfSelected = true;
        }
        if ( !selfSelected )
        {
          message = "Error: You must select yourself for group events.";
          clientPanel.addAlert( message );
          return;
        }
      }

      usersBooked = scheduleEvent( clientPanel.getNewEventType(),
                                   clientPanel.getNewEventDesc(),
                                   clientPanel.getNewEventStartDate(),
                                   clientPanel.getNewEventEndDate(),
                                   selectedUsers, currentUser,
                                   oldEvent );

      if ( usersBooked == null )
      {
        message = "The event could not be added due to a locked calendar.  "
          + "Please try again later.";
        clientPanel.addAlert( message );
        return;
      }

      /* determine which users were added into this event */
      if ( clientPanel.getNewEventType() == 3 )
      {
        String booked = "";
        String notBooked = "";
        Iterator itr = selectedUsers.iterator();
        while ( itr.hasNext() )
        {
          User u = (User)itr.next();
          boolean isBooked = false;
          Iterator itrBooked = usersBooked.iterator();
          while ( itrBooked.hasNext() )
          {
            User uBooked = (User)itrBooked.next();
            if ( u.getId() == uBooked.getId() )
            {
              isBooked = true;
              if ( booked.compareTo( "" ) == 0 ) 
                 booked = booked + u.getName();
              else booked = booked + ", " + u.getName();
            }
          }
          if ( !isBooked )
          {
            if ( notBooked.compareTo( "" ) == 0 )
              notBooked = notBooked + u.getName();
            else notBooked = notBooked + ", " + u.getName();
          }
        }

        if ( usersBooked.size() > 0 )
        {
          if ( calEvent.getEventId() == CalEvent.EDIT_EVENT )
            message = "The event was edited for the calendar(s).";
          else
            message = "The event was successfully added to the calendar(s).";
          clientPanel.addAlert( message );

          if ( notBooked.compareTo( "" ) != 0 )
          {
            message = "The following user(s) were not available for " +
              "the entire event:  " + notBooked + ".";
            clientPanel.addAlert( message );
          }
          /* this will refresh the current event list */
          clientPanel.setCalendar( clientPanel.getCalendar() );
        }
        else 
        {
          if ( calEvent.getEventId() == CalEvent.EDIT_EVENT )
            message = "The event was deleted.  None of the users "
              + "were available for the new event duration.";
          else
            message = "The event was not created.  None of the users "
              + "were available for the entire event.";
          clientPanel.addAlert( message );
        }
      }
      else
      {
        if ( usersBooked.size() == 1 )
        {
          if ( calEvent.getEventId() == CalEvent.EDIT_EVENT )
            message = "The event was sucessfully edited for your calendar.";
          else
            message = "The event was successfully added to your calendar";
          /* this will refresh the current event list */
          clientPanel.setCalendar( clientPanel.getCalendar() );
        }
        else
        {
          if ( calEvent.getEventId() == CalEvent.EDIT_EVENT )
            message = "The event could not be edited due to a conflict.";
          else
            message = "The event could not be added to your calendar due to "
              + "a conflict.";
        }
        clientPanel.addAlert( message );
      }
      try
      {
        clientPanel.getCalendar().clearMembers();
      }
      catch(IOException ex)
      {
        System.out.println("CalendarClient: calEvent(CREATE/EDIT) -> " + ex);
      }
      clientPanel.resetEventList();
      clientPanel.clearEventPanel();
      selectedUsers = clientPanel.getSelectedUsers();
      clientPanel.listUserEvents( selectedUsers );
      clientPanel.setCreateEventPanelButtonsEnabled();
    }

    /* perform necessary actions to delete an event */
    else if ( calEvent.getEventId() == CalEvent.DELETE_EVENT )
    {
      boolean addToThis = true;
      ArrayList users = new ArrayList();
      users = clientPanel.getUsers();
      if ( clientPanel.getSelectedEvent().getType() == 3 ) addToThis = false;

      try
      {
        if( calendarObjInt != null && calendarObjInt.isConnected() )
        {
          calendarObjInt.scheduleEvent( users, null,
            clientPanel.getSelectedEvent(), addToThis );
        }
      }
      catch(IOException ex)
      {
        System.out.println("CalendarClient: calEvent(DELETE_EVENT) -> " + ex);
      }
      clientPanel.addAlert( "The event has been removed from your calendar." );
      clientPanel.clearEventPanel();
      clientPanel.resetEventList();
      clientPanel.clearSelectedUsers();
    }

    /* focus on the connect button so the enter key can be used */
    else if ( calEvent.getEventId() == CalEvent.FOCUS_CONNECT )
    {
      clientPanel.setFocusConnect();
    }

    /* focus on the create button so the enter key can be used */
    else if ( calEvent.getEventId() == CalEvent.FOCUS_CREATE )
    {
      clientPanel.setFocusCreate();
    }

    /* reset the create event panel for anything besides show or list events */
    if ( calEvent.getEventId() != CalEvent.SHOW_EVENT &&
         calEvent.getEventId() != CalEvent.LIST_EVENTS )
      clientPanel.resetCreateEventPanel();
  }

    public static void main(String argv[]) throws InterruptedException
    {
        /* create a new calendar client object for the user interface */
        CalendarClient client = null;
        try
        {
            client = new CalendarClient( );
        }
        catch(IOException ex)
        {
            System.out.println("CalendarClient: main -> " + ex);
        }
        /* start up the gui and action listener here */
        try
        {
            UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
            MetalLookAndFeel.setCurrentTheme( new DefaultMetalTheme() );
        }
        catch (Exception ex)
        {
            System.out.println("CalendarClient: main -> " + ex);
        }
        /* create add the main client panel to the client user interface */
        ClientPanel clientPanel = new ClientPanel();
        Dimension d = new Dimension( 700, 1000 );
        clientPanel.setSize( d );
        clientPanel.addCalEventListener( client );
        client.setClientPanel( clientPanel );

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.setSize( d );
        frame.setContentPane( clientPanel );
        frame.setResizable( false );
        frame.setTitle( "Calendar Client" );
        frame.pack();
        frame.setVisible( true );

        while ( true )
        {
            boolean found = false;

            try
            {
                ArrayList newUsers = client.list();
                ArrayList oldUsers = clientPanel.getUsers();
                Iterator newIt = newUsers.iterator();
                Iterator oldIt = oldUsers.iterator();
                while ( newIt.hasNext() )
                {
                    found = false;
                    User n = (User)newIt.next();
                    while ( oldIt.hasNext() )
                    {
                        User u = (User)oldIt.next();
                        if ( n.getId() == u.getId() )
                        {
                            found = true;
                            break;
                        }
                    }
                    if ( !found ) break;
                }
                if ( !found ) clientPanel.setUsers( newUsers );
            }
            catch( Exception ioe )
            {
                //ioe.printStackTrace();
            }
            Thread.sleep( 1000 );
        }
    }
}
